using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Xunit;
using ExcelTemplateEngine.Models.TemplateModels;

namespace ExcelTemplateEngine.Tests.Helpers
{
    /// <summary>
    /// 测试断言扩展方法 - 提供专用于模板引擎的断言功能
    /// </summary>
    public static class AssertExtensions
    {
        #region 模板节点断言
        
        /// <summary>
        /// 断言模板节点的基本属性
        /// </summary>
        public static void AssertNodeBasics(TemplateCellNode node, string expectedAddress, 
            string? expectedExpression = null, bool expectedIsDataBinding = false)
        {
            Assert.NotNull(node);
            Assert.Equal(expectedAddress, node.Address);
            Assert.Equal(expectedExpression, node.Expression);
            Assert.Equal(expectedIsDataBinding, node.IsDataBindingCell);
            Assert.NotNull(node.NodeId);
            Assert.NotEmpty(node.NodeId);
        }
        
        /// <summary>
        /// 断言数据绑定节点的属性
        /// </summary>
        public static void AssertDataBindingNode(TemplateCellNode node, string expectedDataSource, 
            string expectedFieldName, ExpansionType expectedExpansionType = ExpansionType.Single)
        {
            Assert.True(node.IsDataBindingCell);
            Assert.Equal(expectedDataSource, node.DataSourcePath);
            Assert.Equal(expectedFieldName, node.FieldName);
            Assert.Equal(expectedExpansionType, node.ExpansionType);
        }
        
        /// <summary>
        /// 断言节点的父子关系
        /// </summary>
        public static void AssertParentChildRelation(TemplateCellNode parent, TemplateCellNode child)
        {
            Assert.Contains(child, parent.Children);
            Assert.Equal(parent, child.Parent);
        }
        
        /// <summary>
        /// 断言节点的多重父节点关系
        /// </summary>
        public static void AssertMultipleParents(TemplateCellNode node, 
            TemplateCellNode? expectedParent = null,
            TemplateCellNode? expectedUpperParent = null, 
            TemplateCellNode? expectedLeftParent = null)
        {
            Assert.Equal(expectedParent, node.Parent);
            Assert.Equal(expectedUpperParent, node.UpperParent);
            Assert.Equal(expectedLeftParent, node.LeftParent);
        }
        
        /// <summary>
        /// 断言节点树的深度结构
        /// </summary>
        public static void AssertTreeStructure(TemplateCellNode root, int expectedDepth, int expectedTotalNodes)
        {
            Assert.True(root.IsRoot);
            Assert.Equal(expectedTotalNodes - 1, root.GetDescendants().Count());
            
            var maxDepth = root.GetDescendants().DefaultIfEmpty(root).Max(n => n.GetDepth());
            Assert.Equal(expectedDepth, maxDepth);
        }
        
        #endregion
        
        #region 样式断言
        
        /// <summary>
        /// 断言样式对象不为空且初始化正确
        /// </summary>
        public static void AssertStyleInitialized(FullCellStyle style)
        {
            Assert.NotNull(style);
            Assert.NotNull(style.Font);
            Assert.NotNull(style.Fill);
            Assert.NotNull(style.Border);
            Assert.NotNull(style.Alignment);
        }
        
        /// <summary>
        /// 断言字体样式
        /// </summary>
        public static void AssertFontStyle(FontInfo font, string? expectedName = null, 
            double? expectedSize = null, bool? expectedBold = null, bool? expectedItalic = null)
        {
            Assert.NotNull(font);
            
            if (expectedName != null)
                Assert.Equal(expectedName, font.Name);
            if (expectedSize.HasValue)
                Assert.Equal(expectedSize.Value, font.Size);
            if (expectedBold.HasValue)
                Assert.Equal(expectedBold.Value, font.Bold);
            if (expectedItalic.HasValue)
                Assert.Equal(expectedItalic.Value, font.Italic);
        }
        
        /// <summary>
        /// 断言样式克隆是否正确
        /// </summary>
        public static void AssertStyleClone(FullCellStyle original, FullCellStyle cloned)
        {
            Assert.NotNull(cloned);
            Assert.NotSame(original, cloned);
            
            // 验证属性值相等
            Assert.Equal(original.IsLocked, cloned.IsLocked);
            Assert.Equal(original.IsHidden, cloned.IsHidden);
            Assert.Equal(original.NumberFormat, cloned.NumberFormat);
            
            // 验证子对象是新实例但值相等
            Assert.NotSame(original.Font, cloned.Font);
            Assert.Equal(original.Font.Name, cloned.Font.Name);
            Assert.Equal(original.Font.Size, cloned.Font.Size);
            Assert.Equal(original.Font.Bold, cloned.Font.Bold);
        }
        
        #endregion
        
        #region 集合断言
        
        /// <summary>
        /// 断言集合包含预期的元素数量和类型
        /// </summary>
        public static void AssertCollectionContent<T>(IEnumerable<T> collection, int expectedCount, 
            Func<T, bool>? predicate = null)
        {
            Assert.NotNull(collection);
            
            var items = collection.ToList();
            Assert.Equal(expectedCount, items.Count);
            
            if (predicate != null)
            {
                Assert.All(items, item => Assert.True(predicate(item)));
            }
        }
        
        /// <summary>
        /// 断言两个集合的顺序和内容相同
        /// </summary>
        public static void AssertSequenceEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual, 
            IEqualityComparer<T>? comparer = null)
        {
            if (comparer == null)
            {
                Assert.Equal(expected, actual);
            }
            else
            {
                Assert.Equal(expected, actual, comparer);
            }
        }
        
        #endregion
        
        #region 异常断言
        
        /// <summary>
        /// 断言抛出特定的模板引擎异常
        /// </summary>
        public static T AssertTemplateException<T>(Action action, string? expectedErrorCode = null) 
            where T : Exception
        {
            var exception = Assert.Throws<T>(action);
            
            if (expectedErrorCode != null && exception is ExcelTemplateEngine.Exceptions.TemplateEngineException templateEx)
            {
                Assert.Equal(expectedErrorCode, templateEx.ErrorCode);
            }
            
            return exception;
        }
        
        /// <summary>
        /// 断言异步操作抛出特定异常
        /// </summary>
        public static async Task<T> AssertTemplateExceptionAsync<T>(Func<Task> action, string? expectedErrorCode = null) 
            where T : Exception
        {
            var exception = await Assert.ThrowsAsync<T>(action);
            
            if (expectedErrorCode != null && exception is ExcelTemplateEngine.Exceptions.TemplateEngineException templateEx)
            {
                Assert.Equal(expectedErrorCode, templateEx.ErrorCode);
            }
            
            return exception;
        }
        
        #endregion
        
        #region 性能断言
        
        /// <summary>
        /// 断言操作在指定时间内完成
        /// </summary>
        public static void AssertExecutionTime(Action action, TimeSpan maxDuration, string? operationName = null)
        {
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            action();
            stopwatch.Stop();
            
            var message = operationName != null 
                ? $"Operation '{operationName}' took too long: {stopwatch.Elapsed}" 
                : $"Operation took too long: {stopwatch.Elapsed}";
                
            Assert.True(stopwatch.Elapsed <= maxDuration, message);
        }
        
        /// <summary>
        /// 断言异步操作在指定时间内完成
        /// </summary>
        public static async Task AssertExecutionTimeAsync(Func<Task> action, TimeSpan maxDuration, string? operationName = null)
        {
            var stopwatch = System.Diagnostics.Stopwatch.StartNew();
            await action();
            stopwatch.Stop();
            
            var message = operationName != null 
                ? $"Operation '{operationName}' took too long: {stopwatch.Elapsed}" 
                : $"Operation took too long: {stopwatch.Elapsed}";
                
            Assert.True(stopwatch.Elapsed <= maxDuration, message);
        }
        
        #endregion
        
        #region 数据断言
        
        /// <summary>
        /// 断言数值在指定范围内
        /// </summary>
        public static void AssertInRange<T>(T value, T min, T max, string? paramName = null) 
            where T : IComparable<T>
        {
            var message = paramName != null 
                ? $"Value '{paramName}' ({value}) is not in range [{min}, {max}]"
                : $"Value ({value}) is not in range [{min}, {max}]";
                
            Assert.True(value.CompareTo(min) >= 0 && value.CompareTo(max) <= 0, message);
        }
        
        /// <summary>
        /// 断言字符串匹配模式
        /// </summary>
        public static void AssertStringPattern(string actual, string pattern, string? message = null)
        {
            var regex = new System.Text.RegularExpressions.Regex(pattern);
            var defaultMessage = $"String '{actual}' does not match pattern '{pattern}'";
            Assert.True(regex.IsMatch(actual), message ?? defaultMessage);
        }
        
        /// <summary>
        /// 断言对象图的相等性（深度比较）
        /// </summary>
        public static void AssertObjectGraphEqual(object expected, object actual, int maxDepth = 5)
        {
            AssertObjectGraphEqualInternal(expected, actual, maxDepth, 0, new HashSet<object>());
        }
        
        private static void AssertObjectGraphEqualInternal(object? expected, object? actual, int maxDepth, int currentDepth, HashSet<object> visited)
        {
            if (currentDepth > maxDepth)
                return;
                
            if (ReferenceEquals(expected, actual))
                return;
                
            if (expected == null || actual == null)
            {
                Assert.Equal(expected, actual);
                return;
            }
            
            var type = expected.GetType();
            if (type != actual.GetType())
            {
                Assert.Equal(expected, actual);
                return;
            }
            
            if (type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type == typeof(decimal))
            {
                Assert.Equal(expected, actual);
                return;
            }
            
            if (visited.Contains(expected))
                return;
                
            visited.Add(expected);
            
            // 比较属性
            foreach (var property in type.GetProperties())
            {
                if (property.CanRead && property.GetIndexParameters().Length == 0)
                {
                    try
                    {
                        var expectedValue = property.GetValue(expected);
                        var actualValue = property.GetValue(actual);
                        AssertObjectGraphEqualInternal(expectedValue, actualValue, maxDepth, currentDepth + 1, visited);
                    }
                    catch (TargetParameterCountException)
                    {
                        // 跳过索引属性
                        continue;
                    }
                }
            }
        }
        
        #endregion
    }
}